home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / winterp-1.13 / src-server / w_classes.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-04  |  23.3 KB  |  522 lines

  1. /* -*-C-*-
  2. ********************************************************************************
  3. *
  4. * File:         w_classes.c
  5. * RCS:          $Header: w_classes.c,v 1.10 91/03/14 03:13:33 mayer Exp $
  6. * Description:  XLISP <--> Motif object and class interface.
  7. * Author:       Niels Mayer, HPLabs
  8. * Created:      Wed Jun 14 16:28:45 1989
  9. * Modified:     Thu Oct  3 20:28:22 1991 (Niels Mayer) mayer@hplnpm
  10. * Language:     C
  11. * Package:      N/A
  12. * Status:       X11r5 contrib tape release
  13. *
  14. * WINTERP Copyright 1989, 1990, 1991 Hewlett-Packard Company (by Niels Mayer).
  15. * XLISP version 2.1, Copyright (c) 1989, by David Betz.
  16. *
  17. * Permission to use, copy, modify, distribute, and sell this software and its
  18. * documentation for any purpose is hereby granted without fee, provided that
  19. * the above copyright notice appear in all copies and that both that
  20. * copyright notice and this permission notice appear in supporting
  21. * documentation, and that the name of Hewlett-Packard and David Betz not be
  22. * used in advertising or publicity pertaining to distribution of the software
  23. * without specific, written prior permission.  Hewlett-Packard and David Betz
  24. * make no representations about the suitability of this software for any
  25. * purpose. It is provided "as is" without express or implied warranty.
  26. *
  27. * HEWLETT-PACKARD AND DAVID BETZ DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
  28. * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  29. * IN NO EVENT SHALL HEWLETT-PACKARD NOR DAVID BETZ BE LIABLE FOR ANY SPECIAL,
  30. * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  31. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  32. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  33. * PERFORMANCE OF THIS SOFTWARE.
  34. *
  35. * See ./winterp/COPYRIGHT for information on contacting the authors.
  36. * Please send modifications, improvements and bugfixes to mayer@hplabs.hp.com
  37. * Post XLISP-specific questions/information to the newsgroup comp.lang.lisp.x
  38. **
  39. ********************************************************************************
  40. */
  41. static char rcs_identity[] = "@(#)$Header: w_classes.c,v 1.10 91/03/14 03:13:33 mayer Exp $";
  42.  
  43. /*
  44.  * <limits.h> defines machine dependent limits on sizes of numbers, if your
  45.  * machine doesn't have this, then your compiler doesn't conform to standards
  46.  * XPG2, XPG3, POSIX.1, FIPS 151-1 and you should complain to the manufacturer.
  47.  * 
  48.  * If for some reason your system isn't standards-conforming, you may work
  49.  * around this problem by using the following definitions (assuming 32 bit machine):
  50.  * 
  51.  * #define LONG_MIN (-2147483647 - 1)
  52.  */
  53. #include <limits.h>
  54.  
  55. #include <stdio.h>
  56. #include <Xm/Xm.h>
  57. #include "winterp.h"
  58. #include "user_prefs.h"
  59. #include "xlisp/xlisp.h"
  60.  
  61. #ifdef WINTERP_MOTIF_11
  62. /*
  63.  * For Motif 1.1, <X11/Intrinsic.h> included by <Xm/Xm.h> uses "fast subclassing"
  64.  * to implement XtIsShell(), therefore we don't need to declare shellWidgetClass.
  65.  */
  66. #else                /* MOTIF 1.0 */
  67.  extern WidgetClass shellWidgetClass; /* For Motif 1.0 this is needed by XtIsShell() macro from Intrinsics.h */
  68. #endif                /* WINTERP_MOTIF_11 */
  69.  
  70.  
  71. /******************************************************************************
  72.  * This is called from xldmem.c:sweep() when a WIDGETOBJ gets garbage collected.
  73.  * If the WIDGETOBJ gets gc'd, we must clear the backpointer to the WIDGETOBJ
  74.  * stored in XmNuserData resource of the widget -- after garbage collection, that
  75.  * pointer will be invalid.
  76.  *
  77.  * NOTE: this isn't really needed anymore now that every widget created
  78.  * in winterp is saved in v_savedobjs. These widgets will not be garbage
  79.  * collected until after they are destroyed, at which point get_widgetobj_widgetID()
  80.  * will return NIL and this proc won't do anything. Still, I'm leaving this around
  81.  * incase I decide to make saving every widget in v_savedobjs an optional feature.
  82.  * You want to save all widgets created if you're using the 'get_moused_widget'
  83.  * primitive alot to interactively build your intrface, or if your code violates
  84.  * my initial assumption that each callbackobj corresponds to a single widget.
  85.  * In the case of the rowcolumn widget's XmNentryCallback, that assumption
  86.  * doesn't hold, so I decided to save all active widgetobjs in v_savedobjs
  87.  * to avoid potential bugs resulting from garbage collection of widgets still
  88.  * referenced inside the toolkit.....
  89.  ******************************************************************************/
  90. void Wcls_Garbage_Collect_WIDGETOBJ(o_widget)
  91.      LVAL o_widget;        /* XLTYPE_WIDGETOBJ */
  92. {
  93.   Widget widget_id;
  94.   
  95.   if (widget_id = get_widgetobj_widgetID(o_widget)) { /* make sure widget hasn't been XtDestroyWidget()'d */
  96.     ARGLIST_RESET(); ARGLIST_ADD(XmNuserData, NULL);
  97.     XtSetValues(widget_id, ARGLIST());
  98.   }
  99. }
  100.  
  101.  
  102. /******************************************************************************
  103.  * Wcls_Widget_Destroy_Callback() -- 
  104.  * When a widget gets destroyed, we set the WIDGETOBJ's widgetID field to
  105.  * NIL to mark that the WIDGETOBJ's associated widget got destroyed. This
  106.  * ensures that operations on any WIDGETOBJ's that are sitting around
  107.  * (i.e. referenced in a user's variable) give an appropriate error.
  108.  * Widgets can be destroyed via XtDestroyWidget (== method :DESTROY on 
  109.  * WIDGET_CLASS). This callback will also get called on any child
  110.  * widget of a widget that got XtDestroyWidget()'d.
  111.  * 
  112.  * Additionally, destroying a widget will remove the associated WIDGETOBJ
  113.  * from v_savedobjs so that it may be garbage collected (if not referenced
  114.  * elsewhere). This in turn will allow the garbage collection of any
  115.  * PIXMAP_REFOBJ and CALLBACKOBJs that are referenced implicitly inside the
  116.  * Motif toolkit implementation of widgets.
  117.  ******************************************************************************/
  118. void Wcls_Widget_Destroy_CallbackProc(widgetID, client_data, call_data)
  119.      Widget    widgetID;
  120.      XtPointer client_data;    /* really an LVAL of type XLTYPE_WIDGETOBJ */
  121.      XtPointer call_data;
  122. {
  123.   LVAL o_widget;
  124.   o_widget = (LVAL) client_data;
  125.   set_widgetobj_widgetID(o_widget, NULL); /* mark widgetobject as invalid */
  126.  
  127.   /*
  128.    * remove from v_savedobjs all WIDGETOBJ, CALLBACKOBJ, or PIXMAP_REFOBJ
  129.    * corresponding to the destroyed widget ... this will allow them
  130.    * to be garbage collected next time gc() gets called.
  131.    */
  132.   {
  133.     extern LVAL v_savedobjs;
  134.     int  i = Wso_Hash(o_widget);
  135.     LVAL l_hbucket = getelement(v_savedobjs, i); /* a list of saved objects belonging to this hashbucket */
  136.     LVAL obj;
  137.     LVAL l_prev = NIL;
  138.     while (l_hbucket)         /* while there are elements in the hashbucket */
  139.       if ((obj = car(l_hbucket)) /* <obj> points to cur elt which is non-NIL */
  140.       && ((o_widget == obj)    /* is <obj> the WIDGETOBJ being destroyed? */
  141.           || ((ntype(obj) == XLTYPE_CALLBACKOBJ) /* or is <obj> a CALLBACKOBJ on this widget? */
  142.           && (get_callback_widget(obj) == o_widget))
  143.           || ((ntype(obj) == XLTYPE_PIXMAP_REFOBJ) /* or is <obj> a PIXMAPREFOBJ on this widget? */
  144.           && (get_pixref_widget(obj) == o_widget))
  145.           || ((ntype(obj) == XLTYPE_EVHANDLEROBJ) /* or is <obj> a EVHANDLEROBJ on this widget? */
  146.           && (get_evhandler_widget(obj) == o_widget))
  147.           )) {
  148.     l_hbucket = cdr(l_hbucket); /* l_hbucket now points to next elt or NIL */
  149.     if (!l_prev)
  150.       setelement(v_savedobjs, i, l_hbucket); /* remove first, head is now next elt */
  151.     else
  152.       rplacd(l_prev, l_hbucket); /* remove cur, point previous to next */
  153.       }
  154.       else {            /* go to next elt in hashbucket */
  155.     l_prev = l_hbucket;
  156.     l_hbucket = cdr(l_hbucket);
  157.       }
  158.   }
  159. }
  160.  
  161.  
  162. /******************************************************************************
  163.  * This procedure is meant to be called in an :ISNEW instance initializer
  164.  * method for any widget instances created as subclasses of WIDGET_OBJECT.
  165.  *
  166.  * This procedure initializes a WIDGETOBJ by initializing the WidgetID slot.
  167.  * It changes the node type generated by xlobj.c:clnew() from OBJECT to
  168.  * XLTYPE_WIDGETOBJ so that we can tell that this is a special kind of object.
  169.  * Finally, we save the  WIDGETOBJ in v_savedobjs[] so that the WIDGETOBJ
  170.  * doesn't get garbage collected. WIDGETOBJ's may be garbage collected after
  171.  * they are explicitly destroyed by the Xtoolkit, therefore we set up the
  172.  * XmNdestroyCallback to destroy WIDGETOBJ and any other lisp values
  173.  * (CALLBACKOBJs, PIXMAP_REFOBJs) implicitly referenced inside the
  174.  * Motif toolkit implementation of widgets.
  175.  ******************************************************************************/
  176. void Wcls_Initialize_WIDGETOBJ(o_widget, widget_id)
  177.      LVAL o_widget;        /* OBJECT/XLTYPE_WIDGETOBJ */
  178.      Widget widget_id;
  179. {
  180.   set_widgetobj_widgetID(o_widget, widget_id);
  181.   o_widget->n_type = XLTYPE_WIDGETOBJ; /* OBJECT o_widget is now really a WIDGETOBJ */
  182.   XtAddCallback(widget_id, XmNdestroyCallback, Wcls_Widget_Destroy_CallbackProc, (XtPointer) o_widget);
  183.  
  184.   /*
  185.    * Enter the WIDGETOBJ in v_savedobjs, so that it gets mark()'d.
  186.    * This way, it won't be garbage collected while the widget is 
  187.    * around. Wcls_Widget_Destroy_CallbackProc() above will remove
  188.    * the WIDGETOBJ when it's widget no  longer exists, thereby
  189.    * allowing it and any lisp objects it references to be garbage
  190.    * collected.
  191.    */
  192.   { 
  193.     int  i = Wso_Hash(o_widget);
  194.     LVAL l_hbucket;
  195.     extern LVAL v_savedobjs;
  196.     
  197.     xlsave1(l_hbucket);
  198.     l_hbucket = cons(o_widget, getelement(v_savedobjs, i));
  199.     setelement(v_savedobjs, i, l_hbucket);
  200.     xlpop();
  201.   }
  202. }
  203.  
  204.  
  205. /******************************************************************************
  206.  * Given a widget_id, this subroutine will return an XLISP widget-object
  207.  * instance. 
  208.  * This routine does the following:
  209.  *  0) Check to see if widget_id is a shell widget. If so, then we let wc_SHELL.c
  210.  *     lookup the shell widgetobj in it's hashtable. That's because XmNuserData
  211.  *     is not a valid resource for shell widgets. 
  212.  *  1) if the XmNuserData resource on the widget is non-NIL, we take the
  213.  *     value as the pointer to the widgetOBJ. (note that this resource
  214.  *     can be NIL (by default) for a widgetID that wasn't created via
  215.  *     winterp, or for a widgetOBJ that got garbage collected (see
  216.  *     Wcls_Garbage_Collect_WIDGETOBJ() above)
  217.  *  2) if XmNuserData resource is NIL, we must find the WINTERP widget-class
  218.  *     object associated with the widget and create a new instance of that
  219.  *     class around the widgetID, and return this widgetOBJ.
  220.  *     To do this, we call XtClass(widgetID) to get a pointer to the 
  221.  *     widget's class structure. During initialization of the WINTERP
  222.  *     widget-class objects, we have set up a table of associated widgetclassID's
  223.  *     WIDGETCLASSOBJ-ID's for use by Wcls_WidgetClassID_To_WIDGETCLASSOBJ().
  224.  *     From that fn's result, we get the WINTERP widget-class and create a
  225.  *     "wrapper" widget instance.
  226.  *  3) if the widget-class object is NIL or invalid, then we create a fake
  227.  *     generic widgetobj which is of class o_WIDGET_CLASS.
  228.  ******************************************************************************/
  229. LVAL Wcls_WidgetID_To_WIDGETOBJ(widget_id)
  230.      Widget widget_id;        /* assume that widget_id is a valid Widget, not NULL */
  231. {
  232.   extern LVAL Wcls_WidgetClassID_To_WIDGETCLASSOBJ(); /* def'd below */
  233.   extern LVAL Wshl_WidgetID_To_WIDGETOBJ(); /* from wc_SHELL.c */
  234.   extern LVAL o_WIDGET_CLASS;    /* from wc_WIDGET.c */
  235.   LVAL o_widget, o_widgetclass;
  236.   XtPointer user_data = (XtPointer) LONG_MIN; /* LONG_MIN from <limits.h> */
  237.  
  238.   if (!widget_id)
  239.     return (NIL);
  240.  
  241.   /* (0) check to see if the widget is a shell, if so, then look up widgetobj in table */
  242.   if (XtIsShell(widget_id)) {
  243.     return (Wshl_WidgetID_To_WIDGETOBJ(widget_id));
  244.   }
  245.  
  246.   /* (1) try to get valid widgetobj from XmNuserData backpointer. */
  247.   ARGLIST_RESET(); ARGLIST_ADD(XmNuserData, &user_data);
  248.   XtGetValues(widget_id, ARGLIST());
  249.   if (user_data == (XtPointer) LONG_MIN) /* make sure that XmNuserData resource actually exists on the widget in question. */
  250.     xlfail("Internal error in Wcls_WidgetID_To_WidgetObj() -- couldn't retrieve XmNuserData resource from widget. Motif bug?");
  251.   if ((o_widget = (LVAL) user_data) != NIL) {
  252.     if (ntype(o_widget) == XLTYPE_WIDGETOBJ) /* quick sanity check */
  253.       return (o_widget);
  254.     else
  255.       xlfail("Internal error in WidgetID_To_WidgetObj() -- someone's been messing with this widget's XmNuserData resource!");
  256.   }
  257.   /* Its not a shell, and XmNuserData wasn't set, so it must be a child of a Motif composite widget */
  258.   else if (o_widgetclass = Wcls_WidgetClassID_To_WIDGETCLASSOBJ(XtClass(widget_id))) /* returns NIL on failure */
  259.     /* (2): Create a new WIDGETOBJ of the appropriate class. */
  260.     o_widget = newobject(o_widgetclass, WIDGETOBJ_SIZE);
  261.   else {
  262.     /* (3) fail gracefully (i think). */
  263.     errputstr("Warning -- In Wcls_WidgetID_To_WIDGETOBJ() couldn't find a valid\n");
  264.     errputstr("           widgetclass object inside widget classrecord. Creating\n");
  265.     errputstr("           a \"generic\" WIDGETOBJ of class WIDGET_CLASS.\n");
  266.     o_widget = newobject(o_WIDGET_CLASS, WIDGETOBJ_SIZE);
  267.   }
  268.  
  269.   Wcls_Initialize_WIDGETOBJ(o_widget, widget_id);
  270.  
  271.   /* Store the pointer to the new widgetobj in the widget's XmNuserData resource. */
  272.   ARGLIST_RESET(); ARGLIST_ADD(XmNuserData, (XtArgVal) o_widget);
  273.   XtSetValues(widget_id, ARGLIST());  
  274.   return (o_widget);  
  275. }
  276.  
  277.  
  278. /******************************************************************************
  279.  * Fetches and removes a WIDGETOBJ from the argument stack, returning
  280.  * the widgetID. If the WIDGETOBJ has been destroyed or is not initialized,
  281.  * then this will signal an error. <wobj_return> is a pointer to an LVAL,
  282.  * it returns the WIDGETOBJ retrieved from the argument stack.
  283.  ******************************************************************************/
  284. Widget Wcls_Get_WIDGETOBJ_Argument_Returning_Validated_WidgetID(wobj_return)
  285.      LVAL *wobj_return;
  286. {
  287.   Widget widget_id;
  288.  
  289.   if (widget_id = get_widgetobj_widgetID(*wobj_return = xlga_widgetobj()))
  290.     return (widget_id);
  291.   else
  292.     xlerror("Widget object has been :destroy'd or hasn't been initialized by :isnew.", *wobj_return);
  293. }
  294.  
  295.  
  296. /******************************************************************************/
  297. /******************************************************************************/
  298. /******************************************************************************/
  299. /******************************************************************************/
  300.  
  301. /* instance variable numbers for the class 'Class' --
  302.    from xlobj.c -- must update these if changed those! */
  303. #define MESSAGES    0    /* list of messages */
  304. #define IVARS        1    /* list of instance variable names */
  305. #define CVARS        2    /* list of class variable names */
  306. #define CVALS        3    /* list of class variable values */
  307. #define SUPERCLASS    4    /* pointer to the superclass */
  308. #define IVARCNT        5    /* number of class instance variables */
  309.  
  310. /* class variable numbers for classes derived from 'WIDGET_CLASS' */
  311. #define WIDGET_CLASS_ID        0 
  312. #define WIDGET_CLASS_SYM    1
  313. #define DERIVED_WIDGET_CLASS_CVALS_SIZE 2
  314.  
  315. /* 
  316.  * For looking up WIDGETCLASSOBJ<-->WidgetClass, we setup a table of
  317.  * these pairs, which is initialized by
  318.  * Wcls_Create_Subclass_Of_WIDGET_CLASS() and used by other proc's in this
  319.  * module.
  320.  */
  321. typedef struct _WidgetClass_Pair {
  322.   LVAL        widgetclass_OBJ;
  323.   WidgetClass widgetclass_ID;
  324. } WidgetClass_Pair;
  325.  
  326. #define WIDGETCLASS_TABLE_SIZE 50 /* need to increase this if we add more widgetclasses */
  327. static WidgetClass_Pair widgetclass_table[WIDGETCLASS_TABLE_SIZE];
  328. static int widgetclass_table_end_idx = 0; /* holds index of last element in widgetclass_table */
  329.  
  330. /*****************************************************************************
  331.  * This procedure creates a new 'Class' instance which inherits from the
  332.  * base class 'Widget_Class'. This derived class contains no new instance
  333.  * variables. This procedure is equivalent to doing:
  334.  * (set (implode <class_name>) 
  335.  *      (send Class :new
  336.  *                  '()                ;; no IVARS
  337.  *                  '(WIDGET_CLASS_ID  ;; class variable, inited to
  338.  *                                     ;; fixnum value <widgetclass_id>
  339.  *                    WIDGET_CLASS_SYM ;; class variable, inited to 
  340.  *                                     ;; implode(<class_name>)
  341.  *                  Widget_Class       ;; SUPERCLASS
  342.  ****************************************************************************/
  343. LVAL Wcls_Create_Subclass_Of_WIDGET_CLASS(class_name, widgetclass_id)
  344.      char* class_name;
  345.      WidgetClass widgetclass_id;
  346. {
  347.   extern LVAL o_WIDGET_CLASS;    /* from wc_WIDGET.c */
  348.   extern LVAL xlclass();    /* from xlobj.c */
  349.   LVAL self;
  350.   LVAL v_cvals;
  351.   LVAL l_cvars;
  352.  
  353.   /* create 'Class' instance;
  354.      set slot IVARTOTAL = WIDGETOBJ_SIZE (superclass has all slots)
  355.      set slot IVARS = NIL              ;; no IVAR slots in this class
  356.      set slot MESSAGES = NIL           ;; no messages (yet) for this class */
  357.   self = xlclass(class_name, WIDGETOBJ_SIZE); /* note: self won't get gc'd because it is a symbol's value */
  358.   
  359.   /* set slot IVARCNT = 0, since there's no I-Vars in this WIDGET_CLASS
  360.      subclass. Note that xlclass() above sets IVARTOTAL==WIDGETOBJ_SIZE==1
  361.      since WIDGET_CLASS contains a single slot that holds the WidgetID.
  362.      This is not accessible from lisp (since it's just a pointer), but
  363.      is accessible from C via the accessors get_widgetobj_widgetID()
  364.      and set_widgetobj_widgetID */
  365.   setivar(self, IVARCNT, cvfixnum((FIXTYPE)0));
  366.  
  367.   /* set SUPERCLASS slot to o_WIDGET_CLASS ... */
  368.   setivar(self, SUPERCLASS, o_WIDGET_CLASS);
  369.  
  370.   /* set CVARS  */
  371.   xlsave1(l_cvars);
  372.   l_cvars = cons(xlenter("WIDGET_CLASS_SYM"), NIL);
  373.   setivar(self, CVARS, cons(xlenter("WIDGET_CLASS_ID"), l_cvars));
  374.   xlpop(/*l_cvars*/);
  375.  
  376.   /* set CVALS -- set values for WIDGET_CLASS_ID and WIDGET_CLASS_SYM. */
  377.   v_cvals = newvector(DERIVED_WIDGET_CLASS_CVALS_SIZE);
  378.   setivar(self, CVALS, v_cvals); /* note: also prevents gc'ing of v_cvals since <self> is protected */
  379.   setelement(v_cvals, WIDGET_CLASS_ID, cvfixnum((FIXTYPE) widgetclass_id));
  380.   setelement(v_cvals, WIDGET_CLASS_SYM, xlenter(class_name));
  381.  
  382.   /* add <WidgetClass, WIDGETCLASSOBJ> pair to table for future lookups */
  383.   if (widgetclass_table_end_idx >= WIDGETCLASS_TABLE_SIZE)
  384.     xlfatal("Fatal Error in Initialization -- please recompile w_classes.c with a larger WIDGETCLASS_TABLE_SIZE.");
  385.   widgetclass_table[widgetclass_table_end_idx].widgetclass_OBJ = self;
  386.   widgetclass_table[widgetclass_table_end_idx].widgetclass_ID = widgetclass_id;
  387.   widgetclass_table_end_idx++;
  388.   
  389.   return (self);
  390. }
  391.  
  392.  
  393. /*****************************************************************************
  394.  * This routine gets called by xlprint() to print out an identifier for a
  395.  * WIDGETOBJ.
  396.  ****************************************************************************/
  397. Wcls_Print_WIDGETOBJ(fptr, o_widget)
  398.      LVAL fptr;            /* STREAM || USTREAM */
  399.      LVAL o_widget;        /* XLTYPE_WIDGETOBJ */
  400. {
  401.   extern LVAL o_WIDGET_CLASS;    /* from wc_WIDGET.c */
  402.   extern char temptext[];    /* global in winterp.c */
  403.   extern xlputstr();        /* xlprin.c */
  404.   char* classname;
  405.   LVAL v_cvals;
  406.   LVAL o_class = getclass(o_widget);
  407.   LVAL o_prevclass = NIL;
  408.   
  409.   /* loop through superclasses, stopping at WIDGET_CLASS or NIL(superclass of object) */
  410.   while ((o_class != o_WIDGET_CLASS) && (o_class)) {
  411.     o_prevclass = o_class;
  412.     o_class = getivar(o_class, SUPERCLASS);
  413.   }
  414.  
  415.   /*
  416.    * if the loop terminated with o_CLASS==o_WIDGET_CLASS and o_prevclass!=NIL
  417.    * then o_prevclass is the subclass of WIDGET_CLASS created by
  418.    * Wcls_Create_Subclass_Of_WIDGET_CLASS() in which the class variable
  419.    * WIDGET_CLASS_ID was set. So return that value as WidgetClassID, else NULL
  420.    */
  421.   if ((o_class == o_WIDGET_CLASS) && (o_prevclass) && (v_cvals = getivar(o_prevclass, CVALS)))
  422.     classname = (char *) getstring(getpname(getelement(v_cvals, WIDGET_CLASS_SYM)));
  423.   else
  424.     classname = "WIDGET_CLASS";
  425.   
  426.   sprintf(temptext, "#<Object(%s): #", classname);
  427.   xlputstr(fptr, temptext);
  428.   sprintf(temptext, AFMT, (long) o_widget);
  429.   xlputstr(fptr, temptext);
  430.   xlputc(fptr,'>');
  431. }
  432.  
  433.  
  434. /*****************************************************************************
  435.  * This routine accesses the WIDGET_CLASS_ID class variable that was
  436.  * initialized in the above Wcls_Create_Subclass_Of_WIDGET_CLASS(). This
  437.  * routine expects parameter o_class to be a class-object, that is
  438.  * xlobj.c:xlclass_p(o_class) must be true. If your code
  439.  * accidentally gives this routine an object instance rather than a class
  440.  * instance, then you're hosed as the while loop will go trapseing off
  441.  * through memory in a random fashion....
  442.  *
  443.  * This routine may be called with a class object as created by
  444.  * Wcls_Create_Subclass_Of_WIDGET_CLASS(), or it may be called with
  445.  * a class object that is a subclass of a class generated by
  446.  * Wcls_Create_Subclass_Of_WIDGET_CLASS(). In other words, it
  447.  * will work for widget subclasses created in lisp too.
  448.  *
  449.  * NOTE: the current implementation of this routine doesn't lookup
  450.  * info in widgetclass_table[] because it is faster to chain up a
  451.  * few superclasses to get to the WidgetClassID than to look for a
  452.  * matching widetclassID amongst the 40-odd widgetclasses in winterp.
  453.  ****************************************************************************/
  454. WidgetClass Wcls_WIDGETCLASSOBJ_To_WidgetClassID(o_class)
  455.      LVAL o_class;        /* OBJECT satisfying xlclass_p() */
  456. {
  457.   extern LVAL o_WIDGET_CLASS;    /* from wc_WIDGET.c */
  458.   LVAL v_cvals;
  459.   LVAL o_prevclass = NIL;
  460.   
  461.   /* loop through superclasses, stopping at WIDGET_CLASS or NIL(superclass of object) */
  462.   while ((o_class != o_WIDGET_CLASS) && (o_class)) {
  463.     o_prevclass = o_class;
  464.     o_class = getivar(o_class, SUPERCLASS);
  465.   }
  466.  
  467.   /*
  468.    * if the loop terminated with o_CLASS==o_WIDGET_CLASS and o_prevclass!=NIL
  469.    * then o_prevclass is the subclass of WIDGET_CLASS created by
  470.    * Wcls_Create_Subclass_Of_WIDGET_CLASS() in which the class variable
  471.    * WIDGET_CLASS_ID was set. So return that value as WidgetClassID, else NULL
  472.    */
  473.   if ((o_class == o_WIDGET_CLASS) && (o_prevclass) && (v_cvals = getivar(o_prevclass, CVALS)))
  474.     return ((WidgetClass) getfixnum(getelement(v_cvals, WIDGET_CLASS_ID)));
  475.   else
  476.     return ((WidgetClass) NULL);
  477. }
  478.  
  479. /******************************************************************************
  480.  * given a WidgetClass, this returns the WIDGETCLASSOBJ corresponding to that
  481.  * toolkit ID. If it can't find it, it returns NIL.
  482.  ******************************************************************************/
  483. LVAL Wcls_WidgetClassID_To_WIDGETCLASSOBJ(widget_class)
  484.      WidgetClass widget_class;    /* address of widget's class structure */
  485. {
  486.   register int idx;
  487.   
  488.   for (idx = 0 ; (idx < widgetclass_table_end_idx) ; idx++)
  489.     if (widgetclass_table[idx].widgetclass_ID == widget_class)
  490.       return (widgetclass_table[idx].widgetclass_OBJ);
  491.  
  492.   return (NIL);
  493. }
  494.  
  495.  
  496. /******************************************************************************
  497.  * (WIDGETOBJP <expr>)
  498.  * returns T if argument is a WIDGETOBJ, else NIL
  499.  ******************************************************************************/
  500. LVAL Wcls_Prim_WIDGETOBJP()
  501. {
  502.   extern LVAL true;
  503.   LVAL arg = xlgetarg();
  504.   xllastarg();
  505.   return (widgetobj_p(arg) ? true : NIL);
  506. }
  507.  
  508.  
  509. #ifdef DEBUG_WINTERP_1
  510. /******************************************************************************
  511.  * For debugging
  512.  ******************************************************************************/ 
  513. Wcls_Print_WidgetObj_Info(o_widget)
  514.      LVAL o_widget;        /* XLTYPE_WIDGETOBJ */
  515. {
  516.   fprintf(stderr, "Created <WIDGETOBJ:%lx>\n", o_widget);
  517.   fprintf(stderr, "    widgetID = %lx\n", get_widgetobj_widgetID(o_widget));
  518.   fprintf(stderr, "    windowID = %lx\n", XtWindow(get_widgetobj_widgetID(o_widget)));
  519. }
  520. #endif
  521.